﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace MyTestProject {

    public class TestRender : IDisposable {
        GameWindow window;
        Graphics3D g3d;

        public TestRender () {
            window = new GameWindow ();
            g3d = Graphics3D.GetInstance ();
            g3d.SetContext (window.Context);
            g3d.SetViewport (0, 0, 640, 480);
        }

        public void Dispose () {
            window.Dispose ();
        }

        [Fact]
        public void TestRenderVertexShader () {
            var vert = new VertexShader ("dummy.vert");

            g3d.Render (new[] { vert });

            Assert.Equal (true, g3d.GlCache.ContainsKey (vert));

            vert.Dispose ();

            Assert.Equal (false, g3d.GlCache.ContainsKey (vert));
        }

        [Fact]
        public void TestRenderFragmentShader () {
            var frag = new FragmentShader ("dummy.frag");

            g3d.Render (new[] { frag });

            Assert.Equal (true, g3d.GlCache.ContainsKey (frag));

            frag.Dispose ();

            Assert.Equal (false, g3d.GlCache.ContainsKey (frag));
        }

        /**
         * シェーダープログラムがリンクされて適切なUniform変数とAttribute変数がShaderVariableとして利用可能になっていることを確認するテスト
         * (メモ) コンパイラによっては使われていないUniform変数は削除される!!
         * */
        [Fact]
        public void TestRenderShaderProgram () {
            var vert = new VertexShader ("dummy.vert");
            var frag = new FragmentShader ("dummy.frag");
            var prog = new ShaderProgram (vert, frag);

            g3d.Render (new[] { frag });
            g3d.Render (new[] { vert });
            g3d.Render (new[] { prog });

            // Uniform変数
            Assert.Equal (3, prog.UniformCount);
            Assert.Equal (true, prog.Uniforms.Any (x => x.Name == "ModelViewProjectionMatrix"));
            Assert.Equal (true, prog.Uniforms.Any (x => x.Name == "UseConstantColor"));
            Assert.Equal (true, prog.Uniforms.Any (x => x.Name == "ConstantColor"));

            var modelViewProjectionMatrix = prog.Uniforms.First (x => x.Name == "ModelViewProjectionMatrix");
            Assert.Equal ("ModelViewProjectionMatrix", modelViewProjectionMatrix.Name);
            Assert.Equal (ShaderVariableType.Mat4, modelViewProjectionMatrix.Type);
            Assert.Equal (1, modelViewProjectionMatrix.ArraySize);

            var constantColor = prog.Uniforms.First (x => x.Name == "ConstantColor");
            Assert.Equal ("ConstantColor", constantColor.Name);
            Assert.Equal (ShaderVariableType.Vec4, constantColor.Type);
            Assert.Equal (1, constantColor.ArraySize);

            var useConstantColor = prog.Uniforms.First (x => x.Name == "UseConstantColor");
            Assert.Equal ("UseConstantColor", useConstantColor.Name);
            Assert.Equal (ShaderVariableType.Bool, useConstantColor.Type);
            Assert.Equal (1, useConstantColor.ArraySize);

            // Attribute変数
            Assert.Equal (2, prog.AttributeCount);
            Assert.Equal (true, prog.Attributes.Any (x => x.Name == "m3g_Position"));
            Assert.Equal (true, prog.Attributes.Any (x => x.Name == "m3g_Color"));

            var m3g_Position = prog.Attributes.First (x => x.Name == "m3g_Position");
            Assert.Equal ("m3g_Position", m3g_Position.Name);
            Assert.Equal (ShaderVariableType.Vec3, m3g_Position.Type);
            Assert.Equal (1, m3g_Position.ArraySize);

            var m3g_Color = prog.Attributes.First (x => x.Name == "m3g_Color");
            Assert.Equal ("m3g_Color", m3g_Color.Name);
            Assert.Equal (ShaderVariableType.Vec4, m3g_Color.Type);
            Assert.Equal (1, m3g_Color.ArraySize);



            Assert.Equal (true, g3d.GlCache.ContainsKey (prog));

            prog.Dispose ();

            Assert.Equal (false, g3d.GlCache.ContainsKey (prog));
        }

        /**
          * VertexArrayのレンダリングのテスト
          * */
        [Fact]
        public void TestRenderIndexBuffer () {
            var stripLengths = new int[] { 4, 3 };
            var indices      = new int[] { 1, 2, 3, 4, 10, 11, 12 };
            var ibuf         = new IndexBuffer (PrimitiveType.Triangles, stripLengths, indices);

            g3d.Render (new[] { ibuf });

            Assert.Equal (true, g3d.GlCache.ContainsKey (ibuf));

            ibuf.Dispose ();

            Assert.Equal (false, g3d.GlCache.ContainsKey (ibuf));
        }

        [Fact]
        public void TestRenderVertexArray () {
            var values = new float[] { 1, 2, 3, 4, 5, 6, 7, 9, 9, 10, 11, 12 };
            var varry = new VertexArray (4, 3, ArrayType.Float);
            varry.Set (0, 4, values);

            g3d.Render (new[] { varry });

            Assert.Equal (true, g3d.GlCache.ContainsKey (varry));

            varry.Dispose ();

            Assert.Equal (false, g3d.GlCache.ContainsKey (varry));
        }

        [Fact]
        public void TestRenderMesh () {
            var positions = new VertexArray (4, 3, ArrayType.Float);
            var colors    = new VertexArray (4, 3, ArrayType.Byte);
            var vbuf      = new VertexBuffer ();
            positions.Set (0, 4, new float[] { 1, -1, 0,
                                               1, 1, 0,
                                               -1, -1, 0,
                                               -1, 1, 0 });
            colors.Set (0, 4, new byte[] { 255, 0, 0,
                                           0, 255, 0,
                                           0, 0, 255,
                                           255, 255, 255 });
            vbuf.SetPositions (positions, 1, new float[] { 0, 0, 0 });
            vbuf.SetColors (colors);

            var stripLengths = new int[] { 4 };
            var indices      = new int[] { 0, 1, 2, 3 };
            var ibuf         = new IndexBuffer (PrimitiveType.Triangles, stripLengths, indices);
            var app          = new ShaderAppearance ();
            var frag         = new FragmentShader ("dummy.frag");
            var vert         = new VertexShader ("dummy.vert");
            var shaderProg   = new ShaderProgram (vert, frag);
            var msh          = new Mesh (1, 0);

            app.SetShaderProgram (shaderProg);
            msh.SetVertexBuffer (vbuf);
            msh.SetIndexBuffer (0, ibuf);
            msh.SetShaderAppearance (0, app);

            g3d.Render (new[] { positions, colors });
            g3d.Render (new[] { frag });
            g3d.Render (new[] { vert });
            g3d.Render (new[] { shaderProg });
            g3d.Render (new[] { ibuf });
            g3d.Render (new[] { vbuf });
            g3d.Render (new[] { app });

            g3d.Render (new[] { msh } );

            // 画像チェック
            var bmp = g3d.CaptureScreen ();
            bmp.Save ("TestRender-RenerMesh.png");

            Assert.Equal (true, g3d.GlCache.ContainsKey (app));

            app.Dispose ();

            Assert.Equal (false, g3d.GlCache.ContainsKey (app));
        

        }


    }
}
